#include <xml.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static const char* parse_xmlinfo( const char *buf );
static const char* parse_name( const char *buf, char **name );
static const char* parse_string( const char *buf, char **str );
static const char* parse_head( const char *buf, xml *x, int *have_child );
static xml* parse_tree( const char **buf );
static const char* pass_space( const char *buf );

xml* xml_parse( const char* file )
{
    xml *r = NULL;
    int len;
    char *buf = (char*)malloc( 1024*1024*32 );
    FILE *pf = fopen( file, "r" );
    if( pf != NULL )
    {
        const char *ptr = buf;
        len = fread( buf, 1, 1024*1024*32, pf );
        buf[len] = 0;
        ptr = parse_xmlinfo( buf );
        r = parse_tree( &ptr );
        fclose( pf );
    }
    free( buf );
    return r;
}

void xml_destroy( xml *x )
{
    xml **child = x->child;
    int i = 0;
    while( child[i] != NULL )
    {
        xml_destroy( child[i] );
    }
    i = 0;
    while( x->attr[i] != NULL )
    {
        free( x->attr[i]->name );
        free( x->attr[i]->val );
        free( x->attr[i] );
    }
    free( x->name );
    free( x->child );
    free( x->attr );
    free( x );
}

const char* xml_attr( xml *x, const char *attr )
{
    field **a = x->attr;
    int i = 0;
    while( a[i] != NULL )
    {
        if( strcmp( a[i]->name, attr ) == 0 )
        {
            return a[i]->val;
        }
        ++i;
    }
    return NULL;
}

xml* xml_find( xml *x, const char *name )
{
    xml **child = x->child;
    int i = 0;
    xml *r = NULL;
    if( strcmp( x->name, name ) == 0 )
        return x;
    while( child[i] != NULL )
    {
        r = xml_find( child[i], name );
        if( r != NULL )
            return r;
        ++i;
    }
    if( x->brother != NULL )
        r = xml_find( x->brother, name );
    return r;
}

xml* xml_brother( xml *x )
{
    return x->brother;
}

xml* xml_parent( xml *x )
{
    return x->parent;
}


static const char* parse_xmlinfo( const char *buf )
{
    if( *buf == '<' && buf[1] == '?' )
    {
        while( buf[1] != 0 && (*buf != '?' || buf[1] != '>' ) )
        {
            ++buf;
        }
        if( *buf == '?' )
            buf += 2;
    }
    buf = pass_space( buf );
    return buf;
}

static const char* parse_name( const char *buf, char **name )
{
    static char b[1024];
    char token[256];
    int i;
    memset( token, 0, sizeof(token) );
    for( i = '0'; i <= '9'; ++i )
        token[i] = 1;
    for( i = 'A'; i <= 'Z'; ++i )
        token[i] = 1;
    for( i = 'a'; i <= 'z'; ++i )
        token[i] = 1;
    token['_'] = 1;

    i = 0;
    while( *buf && token[*buf] )
        b[i++] = *(buf++);
    b[i++] = 0;
    *name = (char*)malloc( i );
    strcpy( *name, b );
    return buf;
}

static const char* parse_string( const char *buf, char **str )
{
    static char b[10240];
    int c = 0;
    ++buf;
    while( *buf && *buf != '"' )
    {
        if( *buf == '\\' )
            ++buf;
        b[c++] = *buf;
        ++buf;
    }
    ++buf;
    b[c++] = 0;
    *str = (char*)malloc( c );
    strcpy( *str, b );

    return buf;
}

static const char* parse_head( const char *buf, xml *x, int *have_child )
{
    field fd[1024];
    int cf = 0;
    int i;

    if( *buf != '<' )
        return buf;
    ++buf;
    buf = parse_name( buf, &x->name );
    buf = pass_space( buf );
    while( *buf && *buf != '/' && *buf != '>' )
    {
        buf = parse_name( buf, &fd[cf].name );
        buf = pass_space( buf );
        ++buf;
        buf = pass_space( buf );
        buf = parse_string( buf, &fd[cf].val );
        buf = pass_space( buf );
        ++cf;
    }
    if( *buf == '/' )
    {
        buf += 2;
        *have_child = 0;
    }
    else
    {
        ++buf;
        *have_child = 1;
    }
    buf = pass_space( buf );

    x->attr = (field **)malloc( sizeof(field*) * (cf+1) );
    for( i = 0; i < cf; ++i )
    {
        x->attr[i] = (field *)malloc( sizeof(field) );
        x->attr[i]->name = fd[i].name;
        x->attr[i]->val = fd[i].val;
    }
    x->attr[i] = NULL;

    return buf;
}

static xml* parse_tree( const char **buf )
{
    xml *child[1024];
    int c = 0;
    int have_child;
    xml *x = NULL;

    if( *buf == 0 )
        return x;

    x = (xml *)malloc( sizeof(xml) );
    *buf = parse_head( *buf, x, &have_child );
    if( have_child )
    {
        while( **buf && ( **buf != '<' || (*buf)[1] != '/' ) )
        {
            child[c] = parse_tree( buf );
            child[c]->parent = x;
            child[c]->brother = NULL;
            if( c > 0 )
                child[c-1]->brother = child[c];
            ++c;
        }
        if( **buf )
        {
            *buf += 2;
            while( **buf && **buf != '>' )
                ++*buf;
            if( **buf )
                ++*buf;
        }
        *buf = pass_space( *buf );
        x->child = (xml **)malloc( sizeof(xml*) * (c+1) );
        memcpy( x->child, child, sizeof(xml*) * c );
        x->child[c] = NULL;
    }
    else
    {
        x->child = (xml **)malloc( sizeof(xml*) );
        x->child[0] = NULL;
    }
    x->parent = NULL;
    x->brother = NULL;
    return x;
}

static const char* pass_space( const char *buf )
{
    while( *buf && ( *buf == ' ' || *buf == '\t' || *buf == '\r' || *buf == '\n' ) )
    {
        ++buf;
    }
    return buf;
}

